package com.qf.service.impl;

import cn.hutool.core.util.IdUtil;
import cn.hutool.jwt.JWTUtil;
import com.qf.config.RabbitmqKey;
import com.qf.config.RedisKey;
import com.qf.dao.DrugOrderItemMapper;
import com.qf.dao.DrugOrderMapper;
import com.qf.pojo.entity.MedicleOrder;
import com.qf.pojo.entity.MedicleOrderitem;
import com.qf.pojo.entity.MedicleSeckilldrug;
import com.qf.pojo.req.ReqSeckillDrug;
import com.qf.pojo.resp.Resultmsg;
import com.qf.service.SeckillDrugService;
import com.qf.util.DateUtil;
import com.qf.util.UserUtil;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.amqp.AmqpException;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessagePostProcessor;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import javax.servlet.http.HttpServletRequest;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * @ClassName SeckillDrugServiceImpl
 * @Description
 * @Author lanzhike
 * @Date 2023/8/16/0016 下午 07:52
 * @Version 1.0
 */
@Service
public class SeckillDrugServiceImpl implements SeckillDrugService {

    @Autowired
    RedisTemplate redisTemplate;
    @Autowired
    StringRedisTemplate stringRedisTemplate;
    @Autowired(required = false)
    RedissonClient redissonClient;
    @Autowired(required = false)
    DrugOrderMapper drugOrderMapper;
    @Autowired(required = false)
    DrugOrderItemMapper drugOrderItemMapper;
    @Autowired
    RabbitTemplate rabbitTemplate;
    @Override
    public Resultmsg findData() {
        List<Date> dates = DateUtil.getDateMenus();
        ArrayList<String> list = new ArrayList<>();
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        for (Date date : dates) {
            String data = simpleDateFormat.format(date);
            list.add(data);
        }
        return Resultmsg.success(list);
    }

    @Override
    public Resultmsg findSeckillDrug(ReqSeckillDrug seckillDrug) {
        String key = DateUtil.date2Str(seckillDrug.getDate());
        Boolean aBoolean = redisTemplate.hasKey(RedisKey.KILL_DRUG_KEY + key);
        if (!aBoolean){
            return Resultmsg.fail("当前时间段没有商品");
        }
        List values = redisTemplate.boundHashOps(RedisKey.KILL_DRUG_KEY + key).values();
        return Resultmsg.success(values);
    }

    @Override
    public Resultmsg seckill(ReqSeckillDrug seckillDrug, HttpServletRequest request) {
        //        解析用户id
        String uid = UserUtil.getUserId(request);
//        判断当前时间,是否在秒杀时间段内
        Date date = new Date();
        if (!seckillDrug.getDate().before(date)){
            return Resultmsg.fail("秒杀尚未开始");
        }
        Date date1 = DateUtil.addDateHour(seckillDrug.getDate(), 2);
        if (!date1.after(date)){
            return Resultmsg.fail("秒杀已结束");
        }
//        前端时间
        String key = DateUtil.date2Str(seckillDrug.getDate());
//        Boolean lock = stringRedisTemplate.opsForValue().setIfAbsent(RedisKey.SCHEDLE_INITIAL_KEY, "lock", 3, TimeUnit.SECONDS);
        RLock lock = redissonClient.getLock(RedisKey.SCHEDLE_INITIAL_KEY+seckillDrug.getDrugId());
        try {
            boolean b = lock.tryLock(10, TimeUnit.SECONDS);
            lock.lockInterruptibly(5,TimeUnit.MILLISECONDS);
            if (b) {
//        判断商品库存是否大于0
                String s = stringRedisTemplate.opsForValue().get(RedisKey.KILL_DRUG_NUMBER+key +":" + seckillDrug.getDrugId().toString());
                if (StringUtils.isEmpty(s)) {
                    return Resultmsg.fail("当前时间没有商品");
                }
                Integer integer = Integer.valueOf(s);
                if (integer <= 0) {
                    return Resultmsg.fail("商品已卖空");
                }
//        修改库存商品
                MedicleSeckilldrug seckilldrug = (MedicleSeckilldrug) redisTemplate.boundHashOps(RedisKey.KILL_DRUG_KEY+ key).get(seckillDrug.getDrugId().toString());
                seckilldrug.setSeckillDrugnum(seckilldrug.getSeckillDrugnum() - 1);
                redisTemplate.boundHashOps(RedisKey.KILL_DRUG_KEY + key).put(seckillDrug.getDrugId().toString(), seckilldrug);
//        修改数量
                stringRedisTemplate.opsForValue().decrement(RedisKey.KILL_DRUG_NUMBER + key +":"+ seckillDrug.getDrugId());

                long transactionId = IdUtil.getSnowflakeNextId();
                //生产订单项
                MedicleOrderitem medicleOrderitem = new MedicleOrderitem();
                medicleOrderitem.setUserId(Integer.valueOf(uid));
                medicleOrderitem.setDrugId(seckilldrug.getDrugId());
                medicleOrderitem.setOrderitemMoney(String.valueOf(seckilldrug.getSeckillPrice()));
                medicleOrderitem.setOrderitemNum(1);
                medicleOrderitem.setTransactionId(String.valueOf(transactionId));
                drugOrderItemMapper.insert(medicleOrderitem);
                List list = new ArrayList();
                list.add(medicleOrderitem);
                //        生成订单
                MedicleOrder medicleOrder = new MedicleOrder();
                medicleOrder.setTransactionId(transactionId+"");
                medicleOrder.setOrderMoney(medicleOrderitem.getOrderitemMoney());
                medicleOrder.setUserId(Integer.valueOf(uid));
                medicleOrder.setOrderStatus(0+"");
                medicleOrder.setPayType(seckillDrug.getPayType());
                medicleOrder.setOrderFromaddr(seckillDrug.getFromAddr());
                medicleOrder.setOrderToaddr(seckillDrug.getToAddr());
                medicleOrder.setCreateTime(new Date());
                medicleOrder.setOrderItems(list);
                drugOrderMapper.insert(medicleOrder);
//            生成订单后,redis中,商品库存-1,但若出现未支付退出情况,数据库的库存数量与redis中商品库存数量不一致
//            因为在点击支付宝支付后,会生成订单,将会执行上述操作,该问题为少买问题
//            解决方案,在此处设置延迟队列,5分钟后,再查询数据库商品库存,若未变化,则视为未支付,将redis中库存修改,该订单修改为取消支付
                rabbitTemplate.convertAndSend("dead-exchange", RabbitmqKey.RABBITMQ_DEAD_KEY, transactionId, new MessagePostProcessor() {
                    @Override
                    public Message postProcessMessage(Message message) throws AmqpException {
                        message.getMessageProperties().setExpiration("12000");
                        return message;
                    }
                });
                return Resultmsg.success(transactionId);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            boolean locked = lock.isLocked();
            if (locked){
                lock.unlock();
            }

        }
        return Resultmsg.fail("当前访问人数过多");
    }
}
